#pragma once

#include <iostream>
#include <queue>
#include <pthread.h>

namespace ns_thread_pool
{
    const int g_num = 5;
    template <class T>
    class ThreadPool
    {

    private:
        std::queue<T> _task_queue;
        int _num;
        pthread_mutex_t _mtx;
        pthread_cond_t _cond;
        static ThreadPool<T> *ins; //单例

        ThreadPool(int num = g_num) : _num(num)
        {
            pthread_mutex_init(&_mtx, nullptr);
            pthread_cond_init(&_cond, nullptr);
        }

    public:
        // 单例代码，只new一个对象，调用多个线程
        static ThreadPool<T> *GetInstance()
        {
            static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
            if (ins == nullptr)
            {
                pthread_mutex_lock(&lock);
                if (ins == nullptr)
                {
                    ins = new ThreadPool<T>();
                    ins->InitThreadPool();
                    std::cout << "首次加载对象" << std::endl;
                }
                pthread_mutex_unlock(&lock);
            }
                return ins;
            }
            void Lock()
            {
                pthread_mutex_lock(&_mtx);
            }
            void Unlock()
            {
                pthread_mutex_unlock(&_mtx);
            }
            bool IsEmpty()
            {
                return _task_queue.empty();
            }
            void Wait()
            {
                pthread_cond_wait(&_cond, &_mtx);
            }
            void WakeUp()
            {
                pthread_cond_signal(&_cond);
            }

        public:
            ThreadPool(const ThreadPool<T> &tp) = delete;
            ThreadPool<T> &operator=(ThreadPool<T> &tp) = delete;
            static void *Rountine(void *args)
            {
                pthread_detach(pthread_self());
                ThreadPool<T> *tp = (ThreadPool<T> *)args;
                while (true)
                {
                    tp->Lock();
                    while (tp->IsEmpty())
                    {
                        tp->Wait();
                    }
                    T t;
                    tp->PopTask(&t);
                    tp->Unlock();
                    t.Run();
                }
            }

            void InitThreadPool()
            {
                pthread_t tid;
                for (int i = 0; i < _num; ++i)
                {
                    pthread_create(&tid, nullptr, Rountine, (void *)this);
                }
            }
            void PushTask(const T &in)
            {
                Lock();
                _task_queue.push(in);
                Unlock();
                WakeUp();
            }
            void PopTask(T * out)
            {
                *out = _task_queue.front();
                _task_queue.pop();
            }
            ~ThreadPool()
            {
                pthread_mutex_destroy(&_mtx);
                pthread_cond_destroy(&_cond);
            }
        };
        template <class T>
        ThreadPool<T> *ThreadPool<T>::ins = nullptr;
    }